// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// HTTP客户端实现。参见RFC 7230至7235。
// 
// 这是往返者的低层传输实现。
// 高级接口在客户端。去

package http

import (
	"bufio"
	"compress/gzip"
	"container/list"
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"internal/godebug"
	"io"
	"log"
	"net"
	"net/http/httptrace"
	"net/http/internal/ascii"
	"net/textproto"
	"net/url"
	"reflect"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/http/httpguts"
	"golang.org/x/net/http/httpproxy"
)

// DefaultTransport是Transport的默认实现，由DefaultClient使用。它根据需要建立网络连接
// 并缓存它们以供后续调用重用。根据$HTTP_PROXY和$NO_PROXY（或$HTTP_PROXY和
// 。
// /$NO_PROXY）环境变量的指示，它使用HTTP代理
var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	DialContext: defaultTransportDialContext(&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}),
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

// DefaultMaxidleconSperHost是传输的
// MaxidleconSperHost的默认值。
const DefaultMaxIdleConnsPerHost = 2

// Transport是RoundTripper的一个实现，它支持HTTP、
// HTTPS和HTTP代理（用于HTTP或带有CONNECT的HTTPS）。
// 
// 默认情况下，传输缓存连接以供将来重用。
// 这可能会在访问多台主机时留下许多打开的连接。
// 可以使用传输的CloseIdleConnections方法
// 以及MaxIdleConnsPerHost和DisableKeepAlives字段来管理此行为。
// 
// 应该重用传输，而不是根据需要创建传输。
// 对于多个goroutine并发使用，传输是安全的。
// 
// 传输是用于发出HTTP和HTTPS请求的低级原语。
// 有关Cookie和重定向等高级功能，请参阅客户端。
// 
// 传输对HTTP URL使用HTTP/1.1，对HTTPS URL使用HTTP/1.1或HTTP/2 
// 这取决于服务器是否支持HTTP/2、
// 以及传输的配置方式。DefaultTransport支持HTTP/2。
// 要在传输上显式启用HTTP/2，请使用golang。org/x/net/http2 
// 并调用ConfigureTransport。有关HTTP/2的更多信息，请参阅软件包文档。
// 
// 状态代码在1x范围内的响应要么被自动处理
// 要么被忽略。一个异常是HTTP状态码101（交换协议），它被认为是终端状态，并通过往返返回。要查看
// 忽略的1xx响应，请使用httptrace跟踪包的
// ClientTrace。收到XXX回复。
// 
// Transport仅在遇到网络错误时重试请求
// 如果请求是幂等的且没有正文或有其
// 请求。GetBody已定义。如果
// HTTP请求具有HTTP方法GET、HEAD、OPTIONS或TRACE，则认为HTTP请求是幂等的；或者如果他们的
// 头映射包含“幂等键”或“X幂等键”
// 条目。如果幂等键值是一个长度为零的切片，则
// 请求将被视为幂等，但头不会在
// 线路上发送。
type Transport struct {
	idleMu       sync.Mutex
	closeIdle    bool                                // 用户已请求关闭所有空闲连接
	idleConn     map[connectMethodKey][]*persistConn // 最近在末尾使用的
	idleConnWait map[connectMethodKey]wantConnQueue  // 等待getConns 
	idleLRU      connLRU

	reqMu       sync.Mutex
	reqCanceler map[cancelKey]func(error)

	altMu    sync.Mutex   // 守卫更改altProto only 
	altProto atomic.Value // 为零或映射[string]往返器，密钥是URI方案

	connsPerHostMu   sync.Mutex
	connsPerHost     map[connectMethodKey]int
	connsPerHostWait map[connectMethodKey]wantConnQueue // waiting getConns 

	// Proxy指定一个函数，用于为给定的
	// 请求返回代理。如果函数返回非nil错误，
	// 请求将被中止，并返回所提供的错误。
	// 
	// 代理类型由URL方案确定。支持“http”、
	// /“https”和“socks5”。如果方案为空，则假定
	// /“http”。
	// 
	// 如果Proxy为nil或返回nil*URL，则不使用代理。
	Proxy func(*Request) (*url.URL, error)

	// DialContext指定用于创建未加密TCP连接的拨号函数。
	// 如果DialContext为零（下面不推荐使用的拨号也为零），
	// 则传输使用package net拨号。
	// 
	// DialContext与往返调用同时运行。
	// 当前一个连接
	// 结束。
	// 在后一个DialContext完成之前变为空闲时，发起拨号的往返呼叫可能会使用之前拨打的连接
	DialContext func(ctx context.Context, network, addr string) (net.Conn, error)

	// Dial指定用于创建未加密TCP连接的拨号函数。
	// 
	// 拨号与往返通话同时运行。
	// 当前一个连接
	// 结束。
	// 在后一个拨号完成之前变为空闲时，发起拨号的往返呼叫可能会使用前一个拨号的连接
	// 
	// 已弃用：改用DialContext，这允许传输
	// 在不再需要拨号时立即取消拨号。
	// 如果两者都设置了，则DialContext优先。
	Dial func(network, addr string) (net.Conn, error)

	// DialTLSContext指定一个可选的拨号函数，用于为非代理HTTPS请求创建
	// TLS连接。
	// 
	// 如果DialTLSContext为零（并且下面不推荐使用的DialTLS也为零），则使用
	// DialContext和TLSClientConfig。
	// 
	// 如果设置了DialTLSContext，则拨号和DialContext钩子不会用于HTTPS 
	// 请求，并且会忽略TLSClientConfig和TLSHandshakeTimeout 
	// 。回击网。假设Conn已通过TLS握手。
	DialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)

	// DialTLS指定一个可选的拨号函数，用于为非代理HTTPS请求创建
	// TLS连接。
	// 
	// 已弃用：改用DialTLSContext，这允许传输
	// 在不再需要拨号时立即取消拨号。
	// 如果两者都设置了，则拨号上下文优先。
	DialTLS func(network, addr string) (net.Conn, error)

	// TLSClientConfig指定要与
	// TLS一起使用的TLS配置。客户
	// 如果为空，则使用默认配置。
	// 如果非零，默认情况下可能不会启用HTTP/2支持。
	TLSClientConfig *tls.Config

	// TLSHandshakeTimeout指定等待TLS握手的最长时间。零意味着没有超时。
	TLSHandshakeTimeout time.Duration

	// DisableKeepAlives，如果为true，则禁用HTTP KeepAlives，并且
	// 将仅对单个
	// HTTP请求使用与服务器的连接。
	// 
	// 这与类似名称的TCP保持生命无关。
	DisableKeepAlives bool

	// DisableCompression，如果为true，则阻止传输
	// 在请求不包含现有
	// 请求头进行压缩。如果传输在
	// 接受编码值时，使用“接受编码：gzip”
	// 自身上请求gzip，并得到gzipped响应，则它在响应中透明地被
	// 解码。身体但是，如果用户
	// 明确请求gzip，则不会自动
	// 解压缩。
	DisableCompression bool

	// maxidlecons控制所有主机的最大空闲（保持活动）连接数
	// 连接数。零意味着没有限制。
	MaxIdleConns int

	// maxidleconspherhost，如果非零，则控制每个主机要保持的最大空闲
	// （保持活动）连接数。如果为零，则使用
	// DefaultMaxIDleconSperHost。
	MaxIdleConnsPerHost int

	// MaxConnsPerHost可选地限制每个主机的
	// 连接总数，包括拨号状态、
	// 活动状态和空闲状态的连接。如果违反限制，拨号将被阻止。
	// 
	// 零表示没有限制。
	MaxConnsPerHost int

	// IDLECONTIMEOUT是空闲
	// （保持活动）连接在关闭
	// 自身之前保持空闲的最长时间。
	// 零表示没有限制。
	IdleConnTimeout time.Duration

	// ResponseHeaderTimeout，如果非零，则指定在完全
	// 写入请求（包括其正文，如果有）后等待服务器响应头的时间量。此
	// 时间不包括读取响应正文的时间。
	ResponseHeaderTimeout time.Duration

	// ExpectContinueTimeout，如果非零，则指定在完全
	// 写入请求头（如果请求具有
	// “Expected:100 continue”头）后等待服务器第一个响应头的时间量。零表示没有超时，
	// 导致正文立即发送，而
	// 没有等待服务器批准。
	// 此时间不包括发送请求头的时间。
	ExpectContinueTimeout time.Duration

	// TLSNextProto指定在TLS ALPN 
	// 备用协议（如HTTP/2）。如果Transport使用非空协议名拨打TLS连接
	// 协议协商后，传输如何切换到
	// 并且TLSNextProto包含该密钥的
	// 映射条目（例如“h2”），则该函数为
	// 使用请求的权限（例如“example.com”
	// 或“example.com:1234”）和TLS连接调用。函数
	// 必须返回一个RoundTripper，然后处理请求。
	// 如果TLSNextProto不是nil，则不会自动启用HTTP/2支持。
	TLSNextProto map[string]func(authority string, c *tls.Conn) RoundTripper

	// ProxyConnectHeader可选指定在连接请求期间发送给
	// 代理的头。
	// 要动态设置标头，请参阅GetProxyConnectHeader。
	ProxyConnectHeader Header

	// GetProxyConnectHeader（可选）指定一个func，用于在向
	// 头。
	// ip:port目标发送连接请求时返回要发送到proxyURL的
	// 如果返回错误，则传输的往返将失败，并返回该错误。它可以返回（nil，nil）以不添加头。
	// 如果GetProxyConnectHeader为非零，则ProxyConnectHeader为
	// 忽略。
	GetProxyConnectHeader func(ctx context.Context, proxyURL *url.URL, target string) (Header, error)

	// MaxResponseHeaderBytes指定服务器响应
	// 头中允许的响应字节数限制。
	// 
	// Zero表示使用默认限制。
	MaxResponseHeaderBytes int64

	// WriteBufferSize指定写入传输时使用的写入缓冲区的大小。
	// 如果为零，则使用默认值（当前为4KB）。
	WriteBufferSize int

	// ReadBufferSize指定从传输读取时使用的读取缓冲区的大小。
	// 如果为零，则使用默认值（当前为4KB）。
	ReadBufferSize int

	// nextProtoOnce保护TLSNextProto和
	// h2transport（通过ONCESTNEXTPROTODEFAULTS）
	nextProtoOnce      sync.Once
	h2transport        h2Transport // 如果http2连接了
	tlsNextProtoWasNil bool        // 一旦启动

	// 强制尝试http2控制在非零
	// 拨号时是否启用HTTP/2，或提供DialContext func或TLSClientConfig。
	// 默认情况下，保守地使用这些字段会禁用HTTP/2。
	// 若要使用自定义拨号程序或TLS配置，但仍在尝试HTTP/2 
	// 升级，请将其设置为true。
	ForceAttemptHTTP2 bool
}

// 取消键是reqCanceler映射的键。
// 我们将*请求包装为这种类型，因为我们希望使用原始请求，而不是任何由往返创建的临时请求。
type cancelKey struct {
	req *Request
}

func (t *Transport) writeBufferSize() int {
	if t.WriteBufferSize > 0 {
		return t.WriteBufferSize
	}
	return 4 << 10
}

func (t *Transport) readBufferSize() int {
	if t.ReadBufferSize > 0 {
		return t.ReadBufferSize
	}
	return 4 << 10
}

// 克隆返回t导出字段的深度副本。shtransport是我们期望能够从shttp2调用的接口，它可以从shttp2调用。捆绑在
func (t *Transport) Clone() *Transport {
	t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
	t2 := &Transport{
		Proxy:                  t.Proxy,
		DialContext:            t.DialContext,
		Dial:                   t.Dial,
		DialTLS:                t.DialTLS,
		DialTLSContext:         t.DialTLSContext,
		TLSHandshakeTimeout:    t.TLSHandshakeTimeout,
		DisableKeepAlives:      t.DisableKeepAlives,
		DisableCompression:     t.DisableCompression,
		MaxIdleConns:           t.MaxIdleConns,
		MaxIdleConnsPerHost:    t.MaxIdleConnsPerHost,
		MaxConnsPerHost:        t.MaxConnsPerHost,
		IdleConnTimeout:        t.IdleConnTimeout,
		ResponseHeaderTimeout:  t.ResponseHeaderTimeout,
		ExpectContinueTimeout:  t.ExpectContinueTimeout,
		ProxyConnectHeader:     t.ProxyConnectHeader.Clone(),
		GetProxyConnectHeader:  t.GetProxyConnectHeader,
		MaxResponseHeaderBytes: t.MaxResponseHeaderBytes,
		ForceAttemptHTTP2:      t.ForceAttemptHTTP2,
		WriteBufferSize:        t.WriteBufferSize,
		ReadBufferSize:         t.ReadBufferSize,
	}
	if t.TLSClientConfig != nil {
		t2.TLSClientConfig = t.TLSClientConfig.Clone()
	}
	if !t.tlsNextProtoWasNil {
		npm := map[string]func(authority string, c *tls.Conn) RoundTripper{}
		for k, v := range t.TLSNextProto {
			npm[k] = v
		}
		t2.TLSNextProto = npm
	}
	return t2
}

// h2_捆绑中的传输。go或由用户通过x/net/http2提供。
// 
// 我们用“h2”前缀命名它，以避免使用“http2”前缀
// x/tools/cmd/bundle用于h2_bundle的命名空间。去
type h2Transport interface {
	CloseIdleConnections()
}

func (t *Transport) hasCustomTLSDialer() bool {
	return t.DialTLS != nil || t.DialTLSContext != nil
}

// OnCesterNext ProtoDefaults初始化TLSNextProto。
// 必须通过t.nextprotoone调用它。做
func (t *Transport) onceSetNextProtoDefaults() {
	t.tlsNextProtoWasNil = (t.TLSNextProto == nil)
	if godebug.Get("http2client") == "0" {
		return
	}

	// 如果他们已经用
	// golang配置了http2。org/x/net/http2而不是捆绑拷贝，尝试
	// 获取其http2。传输值（通过“https”
	// altproto映射），因此如果请求，我们可以在其上调用CloseIdleConnections。（第22891期）
	altProto, _ := t.altProto.Load().(map[string]RoundTripper)
	if rv := reflect.ValueOf(altProto["https"]); rv.IsValid() && rv.Type().Kind() == reflect.Struct && rv.Type().NumField() == 1 {
		if v := rv.Field(0); v.CanInterface() {
			if h2i, ok := v.Interface().(h2Transport); ok {
				t.h2transport = h2i
				return
			}
		}
	}

	if t.TLSNextProto != nil {
		// 这是在
		// 传输上禁用http2的有文件证明的方法。
		return
	}
	if !t.ForceAttemptHTTP2 && (t.TLSClientConfig != nil || t.Dial != nil || t.DialContext != nil || t.hasCustomTLSDialer()) {
		// 保守一点，如果他们指定了自定义TLS配置或
		// http2。让他们通过
		// 自定义拨号程序，不要自动启用
		// http2选择加入。配置传输，这样我们就不会因为修改tls而让他们感到惊讶了。配置。第14275期。
		// 但是，如果ForceAttemptHTTP2为真，它将覆盖上述检查。
		return
	}
	if omitBundledHTTP2 {
		return
	}
	t2, err := http2configureTransports(t)
	if err != nil {
		log.Printf("Error enabling Transport HTTP/2 support: %v", err)
		return
	}
	t.h2transport = t2

	// 自动配置http2。传输的MaxHeaderListSize来自
	// http。传输的最大响应头字节。它们的意思并不完全相同，但它们很接近。
	// 
	// TODO:也将其添加到x/net/http2中。在
	// a+build go1后面配置传输。7 build tag:
	if limit1 := t.MaxResponseHeaderBytes; limit1 != 0 && t2.MaxHeaderListSize == 0 {
		const h2max = 1<<32 - 1
		if limit1 >= h2max {
			t2.MaxHeaderListSize = h2max
		} else {
			t2.MaxHeaderListSize = uint32(limit1)
		}
	}
}

// ProxyFromEnvironment返回用于给定请求的代理的URL，如环境变量
// HTTP_proxy、HTTPS_proxy和NO_proxy（或其小写版本
// ）所示。HTTPS 
// 请求的HTTPS_代理优先于HTTP_代理。
// 
// 环境值可以是完整的URL或
// “主机[：端口]”，在这种情况下，假定为“http”方案。
// 支持“http”、“https”和“socks5”方案。
// 如果值的形式不同，则返回错误。
// 
// 如果在
// 环境中未定义代理，或者不应将代理用于给定请求，则返回nil URL和nil错误，
// 由no_proxy定义。
// 
// 作为特例，如果需要。网址。主机为“localhost”（带或不带
// 端口号），则返回nil URL和nil错误。
func ProxyFromEnvironment(req *Request) (*url.URL, error) {
	return envProxyFunc()(req.URL)
}

// ProxyURL返回一个代理函数（用于传输）
// 该函数始终返回相同的URL。
func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) {
	return func(*Request) (*url.URL, error) {
		return fixedURL, nil
	}
}

// transportRequest是一个围绕*请求的包装器，它添加了
// 可选的额外头来写入和存储任何错误，以从往返返回
// 。
type transportRequest struct {
	*Request                         // 原始请求，未经变异
	extra     Header                 // 要写入的额外头，或nababdfg
	cancelKey cancelKey

}

func (tr *transportRequest) extraHeaders() Header {
	if tr.extra == nil {
		tr.extra = make(Header)
	}
	return tr.extra
}

func (tr *transportRequest) setError(err error) {
	tr.mu.Lock()
	if tr.err == nil {
		tr.err = err
	}
	tr.mu.Unlock()
}

func (t *Transport) useRegisteredProtocol(req *Request) bool {
	if req.URL.Scheme == "https" && req.requiresHTTP1() {
		// 如果此请求需要HTTP/1，请不要使用
		// “https”替代协议，如果存在
		// HTTP/2代码接管请求。
		// 现有缓存的HTTP/2连接，该协议由
		return false
	}
	return true
}

// alternateRoundTripper返回备用RoundTripper，如果请求的URL方案需要，则使用
// 来处理该请求，对于使用传输的正常情况，返回
// 或nil。
func (t *Transport) alternateRoundTripper(req *Request) RoundTripper {
	if !t.useRegisteredProtocol(req) {
		return nil
	}
	altProto, _ := t.altProto.Load().(map[string]RoundTripper)
	return altProto[req.URL.Scheme]
}

// roundTrip通过HTTP实现往返程序。
func (t *Transport) roundTrip(req *Request) (*Response, error) {
	t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
	ctx := req.Context()
	trace := httptrace.ContextClientTrace(ctx)

	if req.URL == nil {
		req.closeBody()
		return nil, errors.New("http: nil Request.URL")
	}
	if req.Header == nil {
		req.closeBody()
		return nil, errors.New("http: nil Request.Header")
	}
	scheme := req.URL.Scheme
	isHTTP := scheme == "http" || scheme == "https"
	if isHTTP {
		for k, vv := range req.Header {
			if !httpguts.ValidHeaderFieldName(k) {
				req.closeBody()
				return nil, fmt.Errorf("net/http: invalid header field name %q", k)
			}
			for _, v := range vv {
				if !httpguts.ValidHeaderFieldValue(v) {
					req.closeBody()
					return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k)
				}
			}
		}
	}

	origReq := req
	cancelKey := cancelKey{origReq}
	req = setupRewindBody(req)

	if altRT := t.alternateRoundTripper(req); altRT != nil {
		if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol {
			return resp, err
		}
		var err error
		req, err = rewindBody(req)
		if err != nil {
			return nil, err
		}
	}
	if !isHTTP {
		req.closeBody()
		return nil, badStringError("unsupported protocol scheme", scheme)
	}
	if req.Method != "" && !validMethod(req.Method) {
		req.closeBody()
		return nil, fmt.Errorf("net/http: invalid method %q", req.Method)
	}
	if req.URL.Host == "" {
		req.closeBody()
		return nil, errors.New("http: no Host in request URL")
	}

	for {
		select {
		case <-ctx.Done():
			req.closeBody()
			return nil, ctx.Err()
		default:
		}

		// treq会被往返修改，所以每次重试都需要重新创建。
		treq := &transportRequest{Request: req, trace: trace, cancelKey: cancelKey}
		cm, err := t.connectMethodForRequest(treq)
		if err != nil {
			req.closeBody()
			return nil, err
		}

		// 获取缓存的或新创建的到
		// 主机（用于http或https）、http代理或http代理
		// 预连接到https服务器的连接。在任何情况下，我们都准备好发送请求。
		pconn, err := t.getConn(treq, cm)
		if err != nil {
			t.setReqCanceler(cancelKey, nil)
			req.closeBody()
			return nil, err
		}

		var resp *Response
		if pconn.alt != nil {
			// HTTP/2路径。
			t.setReqCanceler(cancelKey, nil) // 无法使用CancelRequest取消
			resp, err = pconn.alt.RoundTrip(req)
		} else {
			resp, err = pconn.roundTrip(treq)
		}
		if err == nil {
			resp.Request = origReq
			return resp, nil
		}

		// 失败。清理并确定是否重试。
		if http2isNoCachedConnError(err) {
			if t.removeIdleConn(pconn) {
				t.decConnsPerHost(pconn.cacheKey)
			}
		} else if !pconn.shouldRetryRequest(req, err) {
			// 第16465期：基础净收益。康涅狄格州peek的读取错误，
			// 正如我们过去所做的那样。
			if e, ok := err.(nothingWrittenError); ok {
				err = e.error
			}
			if e, ok := err.(transportReadFromServerError); ok {
				err = e.err
			}
			return nil, err
		}
		testHookRoundTripRetried()

		// 如果我们能倒带尸体。
		req, err = rewindBody(req)
		if err != nil {
			return nil, err
		}
	}
}

var errCannotRewind = errors.New("net/http: cannot rewind body after connection loss")

type readTrackingBody struct {
	io.ReadCloser
	didRead  bool
	didClose bool
}

func (r *readTrackingBody) Read(data []byte) (int, error) {
	r.didRead = true
	return r.ReadCloser.Read(data)
}

func (r *readTrackingBody) Close() error {
	r.didClose = true
	return r.ReadCloser.Close()
}

// setupRewindBody返回一个新请求，其中包含一个自定义的正文包装
// 该包装可以报告正文是否需要倒带。
// 当请求
// 没有GetBody但正文还没有被读取时，这可以让rewindBody避免错误结果。
func setupRewindBody(req *Request) *Request {
	if req.Body == nil || req.Body == NoBody {
		return req
	}
	newReq := *req
	newReq.Body = &readTrackingBody{ReadCloser: req.Body}
	return &newReq
}

// rewindBody返回一个新的请求，其中body被重绕。
// 如果主体不需要倒带，则返回未修改的req。
// 倒带体负责关闭请求。Body在适当情况下
// （在所有情况下，倒带Body未经修改返回req时除外）。
func rewindBody(req *Request) (rewound *Request, err error) {
	if req.Body == nil || req.Body == NoBody || (!req.Body.(*readTrackingBody).didRead && !req.Body.(*readTrackingBody).didClose) {
		return req, nil // 无需倒带
	}
	if !req.Body.(*readTrackingBody).didClose {
		req.closeBody()
	}
	if req.GetBody == nil {
		return nil, errCannotRewind
	}
	body, err := req.GetBody()
	if err != nil {
		return nil, err
	}
	newReq := *req
	newReq.Body = &readTrackingBody{ReadCloser: body}
	return &newReq, nil
}

// shouldRetryRequest报告是否应在新连接上重试发送失败的
// HTTP请求。非零输入错误是来自往返的
// 错误。
func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool {
	if http2isNoCachedConnError(err) {
		// 问题16582：如果用户同时启动了一组
		// 请求，它们都可以选择相同的连接
		// 并违反服务器的最大并发流。
		// 请暂时匹配HTTP/1行为，并再次拨打
		// 以获得新的TCP连接，而不是失败
		// 此请求。
		return true
	}
	if err == errMissingHost {
		// 用户错误。
		return false
	}
	if !pc.isReused() {
		// 这是一个新的连接。服务器
		// 没有理由挂断我们的电话。
		// 
		// 此外，如果我们现在重试，我们可以永远循环
		// 创建新连接并重试，如果服务器
		// 因为不喜欢我们的请求（而不是发送错误）而挂断我们的话。
		return false
	}
	if _, ok := err.(nothingWrittenError); ok {
		// 我们从来没有写过任何东西，所以可以安全地重试，如果没有正文，或者我们
		// 可以使用GetBody“倒带”正文。
		return req.outgoingLength() == 0 || req.GetBody != nil
	}
	if !req.isReplayable() {
		// 不要重试非幂等请求。
		return false
	}
	if _, ok := err.(transportReadFromServerError); ok {
		// 我们得到了一些非EOF网络。连接读取失败从服务器读取
		// 第一个响应字节。
		return true
	}
	if err == errServerClosedIdle {
		// 服务器回复io。当我们尝试
		// 阅读回复时，EOF。很可能是在客户端编写请求时发生了一个不幸的keep alive 
		// 超时。
		return true
	}
	return false // 保守的
}

// ErrSkipAltProtocol是由Transport定义的哨兵错误值。注册协议。
var ErrSkipAltProtocol = errors.New("net/http: skip alternate protocol")

// RegisterProtocol向scheme注册了一个新协议。
// 传输将使用给定的方案将请求传递给rt。
// rt负责模拟HTTP请求语义。
// 
// 其他软件包可以使用RegisterProtocol提供
// 协议方案的实现，如“ftp”或“文件”。
// 
// 如果rt.RoundTrip返回ErrSkipAltProtocol，则传输将
// 处理该请求的往返本身，就像
// 协议未注册一样。
func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
	t.altMu.Lock()
	defer t.altMu.Unlock()
	oldMap, _ := t.altProto.Load().(map[string]RoundTripper)
	if _, exists := oldMap[scheme]; exists {
		panic("protocol " + scheme + " already registered")
	}
	newMap := make(map[string]RoundTripper)
	for k, v := range oldMap {
		newMap[k] = v
	}
	newMap[scheme] = rt
	t.altProto.Store(newMap)
}

// CloseIdleConnections关闭之前
// 通过以前的请求连接，但现在处于“保持活动”状态的空闲连接。它不会中断当前
// 正在使用的任何连接。
func (t *Transport) CloseIdleConnections() {
	t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
	t.idleMu.Lock()
	m := t.idleConn
	t.idleConn = nil
	t.closeIdle = true // 关闭新空闲的连接
	t.idleLRU = connLRU{}
	t.idleMu.Unlock()
	for _, conns := range m {
		for _, pconn := range conns {
			pconn.close(errCloseIdleConns)
		}
	}
	if t2 := t.h2transport; t2 != nil {
		t2.CloseIdleConnections()
	}
}

// 取消请求通过关闭连接来取消飞行中的请求。
// 只能在往返返回后调用CancelRequest。
// 
// 已弃用：使用请求。WithContext使用
// 可取消上下文创建请求。CancelRequest无法取消HTTP/2 
// 请求。
func (t *Transport) CancelRequest(req *Request) {
	t.cancelRequest(cancelKey{req}, errRequestCanceled)
}

// 取消飞行中的请求，记录错误值。
// 返回请求是否已取消。
func (t *Transport) cancelRequest(key cancelKey, err error) bool {
	// 在取消功能完成之前，此函数不能返回。
	// 请参阅：https:
	t.reqMu.Lock()
	defer t.reqMu.Unlock()
	cancel := t.reqCanceler[key]
	delete(t.reqCanceler, key)
	if cancel != nil {
		cancel(err)
	}

	return cancel != nil
}

// 
// 私有实现超过此点。
// 

var (
	// proxyConfigOnce guards proxyConfig 
	envProxyOnce      sync.Once
	envProxyFuncValue func(*url.URL) (*url.URL, error)
)

// defaultProxyConfig返回从环境中查找到的proxyConfig值
// 。这降低了在某些平台（例如Windows）上昂贵的查找成本。
func envProxyFunc() func(*url.URL) (*url.URL, error) {
	envProxyOnce.Do(func() {
		envProxyFuncValue = httpproxy.FromEnvironment().ProxyFunc()
	})
	return envProxyFuncValue
}

// resetProxyConfig用于测试。
func resetProxyConfig() {
	envProxyOnce = sync.Once{}
	envProxyFuncValue = nil
}

func (t *Transport) connectMethodForRequest(treq *transportRequest) (cm connectMethod, err error) {
	cm.targetScheme = treq.URL.Scheme
	cm.targetAddr = canonicalAddr(treq.URL)
	if t.Proxy != nil {
		cm.proxyURL, err = t.Proxy(treq.Request)
	}
	cm.onlyH1 = treq.requiresHTTP1()
	return cm, err
}

// proxyAuth返回代理授权标头，以在请求时设置
// 如果适用。
func (cm *connectMethod) proxyAuth() string {
	if cm.proxyURL == nil {
		return ""
	}
	if u := cm.proxyURL.User; u != nil {
		username := u.Username()
		password, _ := u.Password()
		return "Basic " + basicAuth(username, password)
	}
	return ""
}

// 用于调试和测试的错误值，用户看不到。
var (
	errKeepAlivesDisabled = errors.New("http: putIdleConn: keep alives disabled")
	errConnBroken         = errors.New("http: putIdleConn: connection is in bad state")
	errCloseIdle          = errors.New("http: putIdleConn: CloseIdleConnections was called")
	errTooManyIdle        = errors.New("http: putIdleConn: too many idle connections")
	errTooManyIdleHost    = errors.New("http: putIdleConn: too many idle connections for host")
	errCloseIdleConns     = errors.New("http: CloseIdleConnections called")
	errReadLoopExiting    = errors.New("http: persistConn.readLoop exiting")
	errIdleConnTimeout    = errors.New("http: idle connection timeout")

	// 对于幂等请求，用户看不到errServerClosedIdle，但如果服务器关闭空闲连接并从客户端发送FIN 
	// 以及已经写入的POST正文字节，用户可能会看到它。
	// 请参阅https:
	errServerClosedIdle = errors.New("http: server closed idle connection")
)

// transportReadFromServerError由Transport使用。readLoop当
// 1字节peek读取失败时，我们实际上预期会有响应。
// 通常这只是由于固有的保持活动关闭竞争，
// 服务器在客户端
// 写入的同时关闭了连接。基本的err字段通常是io。EOF或
// EconReset之类的东西，因平台而异。但它可能是
// 用户的自定义网络。康涅狄格州也有读取错误，所以我们将其随身携带，以便
// 他们从运输中返回。往返。
type transportReadFromServerError struct {
	err error
}

func (e transportReadFromServerError) Unwrap() error { return e.err }

func (e transportReadFromServerError) Error() string {
	return fmt.Sprintf("net/http: Transport failed to read from server: %v", e.err)
}

func (t *Transport) putOrCloseIdleConn(pconn *persistConn) {
	if err := t.tryPutIdleConn(pconn); err != nil {
		pconn.close(err)
	}
}

func (t *Transport) maxIdleConnsPerHost() int {
	if v := t.MaxIdleConnsPerHost; v != 0 {
		return v
	}
	return DefaultMaxIdleConnsPerHost
}

// tryPutIdleConn将pconn添加到等待新请求的空闲持久连接列表中。
// 如果pconn不再需要或状态不好，tryPutIdleConn将返回
// 一个错误，解释其未注册的原因。
// tryPutIdleConn未关闭pconn。用PutorCloseIdlecon代替。
func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
	if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 {
		return errKeepAlivesDisabled
	}
	if pconn.isBroken() {
		return errConnBroken
	}
	pconn.markReused()

	t.idleMu.Lock()
	defer t.idleMu.Unlock()

	// HTTP/2（pconn.alt！=nil）连接不会从空闲列表中出现，因为多个goroutine可以同时使用它们。
	// 如果这是一个“返回”的HTTP/2连接，我们就完成了。
	if pconn.alt != nil && t.idleLRU.m[pconn] != nil {
		return nil
	}

	// 将pconn发送到goroutine，等待空闲连接（如果有）。
	// （他们可能正在积极拨号，但这个连接首先准备好了。
	// Chrome称这个套接字为延迟绑定。
	// 请参阅https:
	key := pconn.cacheKey
	if q, ok := t.idleConnWait[key]; ok {
		done := false
		if pconn.alt == nil {
			// HTTP/1.
			// 循环等待列表，直到我们找到尚未完成的w，并将其交给pconn。
			for q.len() > 0 {
				w := q.popFront()
				if w.tryDeliver(pconn, nil) {
					done = true
					break
				}
			}
		} else {
			// HTTP/2.
			// 可以将相同的pconn交给等待列表中的每个人。）列表，
			// 我们仍然不会完成：我们希望无条件地将其放入空闲
			// 列表中，以供将来的任何客户使用。
			for q.len() > 0 {
				w := q.popFront()
				w.tryDeliver(pconn, nil)
			}
		}
		if q.len() == 0 {
			delete(t.idleConnWait, key)
		} else {
			t.idleConnWait[key] = q
		}
		if done {
			return nil
		}
	}

	if t.closeIdle {
		return errCloseIdle
	}
	if t.idleConn == nil {
		t.idleConn = make(map[connectMethodKey][]*persistConn)
	}
	idles := t.idleConn[key]
	if len(idles) >= t.maxIdleConnsPerHost() {
		return errTooManyIdleHost
	}
	for _, exist := range idles {
		if exist == pconn {
			log.Fatalf("dup idle pconn %p in freelist", pconn)
		}
	}
	t.idleConn[key] = append(idles, pconn)
	t.idleLRU.add(pconn)
	if t.MaxIdleConns != 0 && t.idleLRU.len() > t.MaxIdleConns {
		oldest := t.idleLRU.removeOldest()
		oldest.close(errTooManyIdle)
		t.removeIdleConnLocked(oldest)
	}

	// 设置空闲计时器，但仅限于HTTP/1（pconn.alt==nil）。
	// HTTP/2实现管理空闲计时器本身
	// （请参阅h2_bundle.go中的idlecontimeout）。
	if t.IdleConnTimeout > 0 && pconn.alt == nil {
		if pconn.idleTimer != nil {
			pconn.idleTimer.Reset(t.IdleConnTimeout)
		} else {
			pconn.idleTimer = time.AfterFunc(t.IdleConnTimeout, pconn.closeConnIfStillIdle)
		}
	}
	pconn.idleAt = time.Now()
	return nil
}

// QueueForIdleCon队列w接收w.cm的下一个空闲连接。
// 作为对调用方的优化提示，QueueForIdlecon报告
// 它成功地传递了一个已经空闲的连接。
func (t *Transport) queueForIdleConn(w *wantConn) (delivered bool) {
	if t.DisableKeepAlives {
		return false
	}

	t.idleMu.Lock()
	defer t.idleMu.Unlock()

	// 停止关闭空闲的连接-我们可能需要一个。
	// （也就是说，撤销t.CloseIdleConnections的影响。）
	t.closeIdle = false

	if w == nil {
		// 发生在测试钩子中。
		return false
	}

	// 如果设置了idlecontimeout，则计算最早的
	// persistConn。空闲时间我们愿意使用缓存的空闲
	// 康涅狄格州
	var oldTime time.Time
	if t.IdleConnTimeout > 0 {
		oldTime = time.Now().Add(-t.IdleConnTimeout)
	}

	// 查找最近使用的空闲连接。
	if list, ok := t.idleConn[w.key]; ok {
		stop := false
		delivered := false
		for len(list) > 0 && !stop {
			pconn := list[len(list)-1]

			// 考虑到
			// 只有墙时间（第（0）轮），如果这是笔记本电脑或虚拟机
			// 使用之前缓存的空闲连接退出暂停，请查看此连接是否空闲太长。
			tooOld := !oldTime.IsZero() && pconn.idleAt.Round(0).Before(oldTime)
			if tooOld {
				// 异步清理。在自己的goroutine中启动（就像
				// time.AfterFunc调用它一样）；它收购了我们持有的idleMu，并建立了一个同步网络。康涅狄格州，完毕。
				go pconn.closeConnIfStillIdle()
			}
			if pconn.isBroken() || tooOld {
				// 如果其中一个。readLoop已将连接标记为
				// 已断开，但无法传输。removeIdleConn尚未将其从空闲列表中删除
				// 或者如果此persistConn太旧（它是
				// 空闲太长），则忽略它并寻找另一个。在这两起
				// 案件中，它已经处于关闭过程中。
				list = list[:len(list)-1]
				continue
			}
			delivered = w.tryDeliver(pconn, nil)
			if delivered {
				if pconn.alt != nil {
					// HTTP/2：多个客户端可以共享pconn。
					// 将其留在列表中。
				} else {
					// HTTP/1:只有一个客户端可以使用pconn。
					// 将其从列表中删除。
					t.idleLRU.remove(pconn)
					list = list[:len(list)-1]
				}
			}
			stop = true
		}
		if len(list) > 0 {
			t.idleConn[w.key] = list
		} else {
			delete(t.idleConn, w.key)
		}
		if stop {
			return delivered
		}
	}

	// 注册以接收下一个空闲连接。
	if t.idleConnWait == nil {
		t.idleConnWait = make(map[connectMethodKey]wantConnQueue)
	}
	q := t.idleConnWait[w.key]
	q.cleanFront()
	q.pushBack(w)
	t.idleConnWait[w.key] = q
	return false
}

// removeIdleConn将pconn标记为已死亡。
func (t *Transport) removeIdleConn(pconn *persistConn) bool {
	t.idleMu.Lock()
	defer t.idleMu.Unlock()
	return t.removeIdleConnLocked(pconn)
}

// t.idleMu必须持有。
func (t *Transport) removeIdleConnLocked(pconn *persistConn) bool {
	if pconn.idleTimer != nil {
		pconn.idleTimer.Stop()
	}
	t.idleLRU.remove(pconn)
	key := pconn.cacheKey
	pconns := t.idleConn[key]
	var removed bool
	switch len(pconns) {
	case 0:
		// 无
	case 1:
		if pconns[0] == pconn {
			delete(t.idleConn, key)
			removed = true
		}
	default:
		for i, v := range pconns {
			if v != pconn {
				continue
			}
			// 向下滑动，最后保留最近使用的
			// 连接。
			copy(pconns[i:], pconns[i+1:])
			t.idleConn[key] = pconns[:len(pconns)-1]
			removed = true
			break
		}
	}
	return removed
}

func (t *Transport) setReqCanceler(key cancelKey, fn func(error)) {
	t.reqMu.Lock()
	defer t.reqMu.Unlock()
	if t.reqCanceler == nil {
		t.reqCanceler = make(map[cancelKey]func(error))
	}
	if fn != nil {
		t.reqCanceler[key] = fn
	} else {
		delete(t.reqCanceler, key)
	}
}

// replaceReqCanceler替换现有的取消函数。如果请求没有取消函数
// 我们不会设置该函数并返回false。
// 由于CancelRequest将清除取消器，我们可以使用返回值来检测
// 自上次setReqCancel调用以来，请求是否已被取消。
func (t *Transport) replaceReqCanceler(key cancelKey, fn func(error)) bool {
	t.reqMu.Lock()
	defer t.reqMu.Unlock()
	_, ok := t.reqCanceler[key]
	if !ok {
		return false
	}
	if fn != nil {
		t.reqCanceler[key] = fn
	} else {
		delete(t.reqCanceler, key)
	}
	return true
}

var zeroDialer net.Dialer

func (t *Transport) dial(ctx context.Context, network, addr string) (net.Conn, error) {
	if t.DialContext != nil {
		return t.DialContext(ctx, network, addr)
	}
	if t.Dial != nil {
		c, err := t.Dial(network, addr)
		if c == nil && err == nil {
			err = errors.New("net/http: Transport.Dial hook returned (nil, nil)")
		}
		return c, err
	}
	return zeroDialer.DialContext(ctx, network, addr)
}

// wantConn记录有关想要的连接的状态
// （即，对getConn的活动调用）。
// 可以通过拨号或查找空闲连接获取连接，
// 或者取消连接可能会导致不再需要连接。
// 这三个选项相互竞争，并使用
// wantConn协调并商定获胜结果。
type wantConn struct {
	cm    connectMethod
	key   connectMethodKey // cm。key（）
	ctx   context.Context  // 拨号上下文
	ready chan struct{}    // 当pc关闭时，发送错误对

	// 用于测试的挂钩，以了解何时完成拨号
	// 当拨号排队时，在getConn goroutine中调用beforeDial。
	// 当拨号完成或取消时，会调用afterDial。
	beforeDial func()
	afterDial  func()

	mu  sync.Mutex // 保护pc，错误，关闭（准备就绪）
	pc  *persistConn
	err error
}

// 等待报告w是否仍在等待应答（连接或错误）。
func (w *wantConn) waiting() bool {
	select {
	case <-w.ready:
		return false
	default:
		return true
	}
}

// tryDeliver尝试将pc发送到w，并报告是否成功。
func (w *wantConn) tryDeliver(pc *persistConn, err error) bool {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.pc != nil || w.err != nil {
		return false
	}

	w.pc = pc
	w.err = err
	if w.pc == nil && w.err == nil {
		panic("net/http: internal error: misuse of tryDeliver")
	}
	close(w.ready)
	return true
}

// 取消将w标记为不再需要结果（例如，由于取消）。
// 如果连接已经被传递，cancel会将其与t.putorCloseIdlecon一起返回。
func (w *wantConn) cancel(t *Transport, err error) {
	w.mu.Lock()
	if w.pc == nil && w.err == nil {
		close(w.ready) // 捕获未来交付中的错误行为
	}
	pc := w.pc
	w.pc = nil
	w.err = err
	w.mu.Unlock()

	if pc != nil {
		t.putOrCloseIdleConn(pc)
	}
}

// WANTCONN队列是WANTCONN的队列。这是一个队列，不是一个队列。
type wantConnQueue struct {
	// 它分为两个阶段——头部[headPos]和尾部。
	// popFront在第一阶段是微不足道的（headPos++），而
	// pushBack在第二阶段是微不足道的（append）。
	// 如果第一级为空，popFront可以交换
	// 第一级和第二级以纠正这种情况。
	// 
	// 这种两阶段拆分类似于在冈崎的纯功能队列中使用两个列表
	// 但没有交换阶段时反转列表的
	// 开销。
	head    []*wantConn
	headPos int
	tail    []*wantConn
}

// len返回队列中的项目数。
func (q *wantConnQueue) len() int {
	return len(q.head) - q.headPos + len(q.tail)
}

// 后推将w添加到队列的后面。
func (q *wantConnQueue) pushBack(w *wantConn) {
	q.tail = append(q.tail, w)
}

// popFront删除并返回队列前面的wantConn。
func (q *wantConnQueue) popFront() *wantConn {
	if q.headPos >= len(q.head) {
		if len(q.tail) == 0 {
			return nil
		}
		// 拿起尾巴作为新的头部，清除尾巴。
		q.head, q.headPos, q.tail = q.tail, 0, q.head[:0]
	}
	w := q.head[q.headPos]
	q.head[q.headPos] = nil
	q.headPos++
	return w
}

// peekFront返回队列前面的wantConn，而不删除它。
func (q *wantConnQueue) peekFront() *wantConn {
	if q.headPos < len(q.head) {
		return q.head[q.headPos]
	}
	if len(q.tail) > 0 {
		return q.tail[0]
	}
	return nil
}

// cleanFront从
// 队列的头部弹出任何不再等待的WANTCONN，报告是否弹出任何WANTCONN。
func (q *wantConnQueue) cleanFront() (cleaned bool) {
	for {
		w := q.peekFront()
		if w == nil || w.waiting() {
			return cleaned
		}
		q.popFront()
		cleaned = true
	}
}

func (t *Transport) customDialTLS(ctx context.Context, network, addr string) (conn net.Conn, err error) {
	if t.DialTLSContext != nil {
		conn, err = t.DialTLSContext(ctx, network, addr)
	} else {
		conn, err = t.DialTLS(network, addr)
	}
	if conn == nil && err == nil {
		err = errors.New("net/http: Transport.DialTLS or DialTLSContext returned (nil, nil)")
	}
	return
}

// getConn拨号并创建一个新的persistConn，作为
// 在connectMethod中指定。这包括进行代理连接
// 和/或设置TLS。如果未返回错误，则persistConn 
// 已准备好向写入请求。
func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persistConn, err error) {
	req := treq.Request
	trace := treq.trace
	ctx := req.Context()
	if trace != nil && trace.GetConn != nil {
		trace.GetConn(cm.addr())
	}

	w := &wantConn{
		cm:         cm,
		key:        cm.key(),
		ctx:        ctx,
		ready:      make(chan struct{}, 1),
		beforeDial: testHookPrePendingDial,
		afterDial:  testHookPostPendingDial,
	}
	defer func() {
		if err != nil {
			w.cancel(t, err)
		}
	}()

	// 空闲连接的队列。
	if delivered := t.queueForIdleConn(w); delivered {
		pc := w.pc
		// 仅对HTTP/1进行跟踪。
		// HTTP/2调用跟踪。哥特康本身。
		if pc.alt == nil && trace != nil && trace.GotConn != nil {
			trace.GotConn(pc.gotIdleConnTrace(pc.idleAt))
		}
		// 将请求取消器设置为一些非零函数，这样我们可以检测从现在到何时它是否被清除
		// 我们进入往返
		t.setReqCanceler(treq.cancelKey, func(error) {})
		return pc, nil
	}

	cancelc := make(chan error, 1)
	t.setReqCanceler(treq.cancelKey, func(err error) { cancelc <- err })

	// 队列以获得拨号权限。
	t.queueForDial(w)

	// 等待完成或取消。
	select {
	case <-w.ready:
		// 跟踪成功，但仅限于HTTP/1。
		// HTTP/2调用跟踪。哥特康本身。
		if w.pc != nil && w.pc.alt == nil && trace != nil && trace.GotConn != nil {
			trace.GotConn(httptrace.GotConnInfo{Conn: w.pc.conn, Reused: w.pc.isReused()})
		}
		if w.err != nil {
			// 如果请求被取消，那可能是
			// 导致w.err的原因；如果是这样，最好返回
			// 取消错误（见golang.org/issue/16049）。
			select {
			case <-req.Cancel:
				return nil, errRequestCanceledConn
			case <-req.Context().Done():
				return nil, req.Context().Err()
			case err := <-cancelc:
				if err == errRequestCanceled {
					err = errRequestCanceledConn
				}
				return nil, err
			default:
				// 返回
			}
		}
		return w.pc, w.err
	case <-req.Cancel:
		return nil, errRequestCanceledConn
	case <-req.Context().Done():
		return nil, req.Context().Err()
	case err := <-cancelc:
		if err == errRequestCanceled {
			err = errRequestCanceledConn
		}
		return nil, err
	}
}

// 排队等待开始拨号的许可。
// 一旦w获得拨号许可，它将在另一个goroutine中进行拨号。
func (t *Transport) queueForDial(w *wantConn) {
	w.beforeDial()
	if t.MaxConnsPerHost <= 0 {
		go t.dialConnFor(w)
		return
	}

	t.connsPerHostMu.Lock()
	defer t.connsPerHostMu.Unlock()

	if n := t.connsPerHost[w.key]; n < t.MaxConnsPerHost {
		if t.connsPerHost == nil {
			t.connsPerHost = make(map[connectMethodKey]int)
		}
		t.connsPerHost[w.key] = n + 1
		go t.dialConnFor(w)
		return
	}

	if t.connsPerHostWait == nil {
		t.connsPerHostWait = make(map[connectMethodKey]wantConnQueue)
	}
	q := t.connsPerHostWait[w.key]
	q.cleanFront()
	q.pushBack(w)
	t.connsPerHostWait[w.key] = q
}

// DialconFor代表w拨号并将结果发送给w。
// DialconFor已获得拨号w.cm的权限，并计入t.connCount[w.cm.key（）]中。
// 如果拨号被取消或不成功，dialConnFor将递减t.connCount[w.cm.key（）]。
func (t *Transport) dialConnFor(w *wantConn) {
	defer w.afterDial()

	pc, err := t.dialConn(w.ctx, w.cm)
	delivered := w.tryDeliver(pc, err)
	if err == nil && (!delivered || pc.alt != nil) {
		// pconn未传递给w，
		// 或者它是HTTP/2，可以共享。
		// 添加到空闲连接池。
		t.putOrCloseIdleConn(pc)
	}
	if err != nil {
		t.decConnsPerHost(w.key)
	}
}

// decConnsPerHost减少密钥的每主机连接计数，
// 这反过来可能会赋予不同的等待goroutine拨号权限。
func (t *Transport) decConnsPerHost(key connectMethodKey) {
	if t.MaxConnsPerHost <= 0 {
		return
	}

	t.connsPerHostMu.Lock()
	defer t.connsPerHostMu.Unlock()
	n := t.connsPerHost[key]
	if n == 0 {
		// 不应该发生，但如果发生，计数就会出现问题，可能会导致沉默的僵局，所以大声报告问题。
		panic("net/http: internal error: connCount underflow")
	}

	// 我们能把这个数字交给一个仍在等待拨号的goroutine吗？
	// （等待列表中的一些goroutines可能已超时或
	// 以另一种方式获得连接。如果它们都消失了，
	// 我们不想启动任何虚假的拨号操作。）
	if q := t.connsPerHostWait[key]; q.len() > 0 {
		done := false
		for q.len() > 0 {
			w := q.popFront()
			if w.waiting() {
				go t.dialConnFor(w)
				done = true
				break
			}
		}
		if q.len() == 0 {
			delete(t.connsPerHostWait, key)
		} else {
			// q是一个值（就像一个切片），所以我们必须将更新后的q存储回地图中。
			t.connsPerHostWait[key] = q
		}
		if done {
			return
		}
	}

	// 否则，减少记录的计数。
	if n--; n == 0 {
		delete(t.connsPerHost, key)
	} else {
		t.connsPerHost[key] = n
	}
}

// 将TLS添加到持久连接，即协商TLS会话。如果pconn已经是TLS 
// 隧道，此函数将在加密通道内建立嵌套的TLS会话。
// 远程终结点的名称可能会被TLSClientConfig覆盖。服务器名。
func (pconn *persistConn) addTLS(ctx context.Context, name string, trace *httptrace.ClientTrace) error {
	// 启动TLS并根据证书检查远程主机名。
	cfg := cloneTLSConfig(pconn.t.TLSClientConfig)
	if cfg.ServerName == "" {
		cfg.ServerName = name
	}
	if pconn.cacheKey.onlyH1 {
		cfg.NextProtos = nil
	}
	plainConn := pconn.conn
	tlsConn := tls.Client(plainConn, cfg)
	errc := make(chan error, 2)
	var timer *time.Timer // 用于取消TLS握手
	if d := pconn.t.TLSHandshakeTimeout; d != 0 {
		timer = time.AfterFunc(d, func() {
			errc <- tlsHandshakeTimeoutError{}
		})
	}
	go func() {
		if trace != nil && trace.TLSHandshakeStart != nil {
			trace.TLSHandshakeStart()
		}
		err := tlsConn.HandshakeContext(ctx)
		if timer != nil {
			timer.Stop()
		}
		errc <- err
	}()
	if err := <-errc; err != nil {
		plainConn.Close()
		if trace != nil && trace.TLSHandshakeDone != nil {
			trace.TLSHandshakeDone(tls.ConnectionState{}, err)
		}
		return err
	}
	cs := tlsConn.ConnectionState()
	if trace != nil && trace.TLSHandshakeDone != nil {
		trace.TLSHandshakeDone(cs, nil)
	}
	pconn.tlsState = &cs
	pconn.conn = tlsConn
	return nil
}

type erringRoundTripper interface {
	RoundTripErr() error
}

func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *persistConn, err error) {
	pconn = &persistConn{
		t:             t,
		cacheKey:      cm.key(),
		reqch:         make(chan requestAndChan, 1),
		writech:       make(chan writeRequest, 1),
		closech:       make(chan struct{}),
		writeErrCh:    make(chan error, 1),
		writeLoopDone: make(chan struct{}),
	}
	trace := httptrace.ContextClientTrace(ctx)
	wrapErr := func(err error) error {
		if cm.proxyURL != nil {
			// 根据第16997期返回键入的错误
			return &net.OpError{Op: "proxyconnect", Net: "tcp", Err: err}
		}
		return err
	}
	if cm.scheme() == "https" && t.hasCustomTLSDialer() {
		var err error
		pconn.conn, err = t.customDialTLS(ctx, "tcp", cm.addr())
		if err != nil {
			return nil, wrapErr(err)
		}
		if tc, ok := pconn.conn.(*tls.Conn); ok {
			// 此处握手，以防DialTLS没有。
			// 下面的TLSNextProto依赖于它来了解连接状态。
			if trace != nil && trace.TLSHandshakeStart != nil {
				trace.TLSHandshakeStart()
			}
			if err := tc.HandshakeContext(ctx); err != nil {
				go pconn.conn.Close()
				if trace != nil && trace.TLSHandshakeDone != nil {
					trace.TLSHandshakeDone(tls.ConnectionState{}, err)
				}
				return nil, err
			}
			cs := tc.ConnectionState()
			if trace != nil && trace.TLSHandshakeDone != nil {
				trace.TLSHandshakeDone(cs, nil)
			}
			pconn.tlsState = &cs
		}
	} else {
		conn, err := t.dial(ctx, "tcp", cm.addr())
		if err != nil {
			return nil, wrapErr(err)
		}
		pconn.conn = conn
		if cm.scheme() == "https" {
			var firstTLSHost string
			if firstTLSHost, _, err = net.SplitHostPort(cm.addr()); err != nil {
				return nil, wrapErr(err)
			}
			if err = pconn.addTLS(ctx, firstTLSHost, trace); err != nil {
				return nil, wrapErr(err)
			}
		}
	}

	// 代理设置。
	switch {
	case cm.proxyURL == nil:
		// 什么也不做。不使用代理。
	case cm.proxyURL.Scheme == "socks5":
		conn := pconn.conn
		d := socksNewDialer("tcp", conn.RemoteAddr().String())
		if u := cm.proxyURL.User; u != nil {
			auth := &socksUsernamePassword{
				Username: u.Username(),
			}
			auth.Password, _ = u.Password()
			d.AuthMethods = []socksAuthMethod{
				socksAuthMethodNotRequired,
				socksAuthMethodUsernamePassword,
			}
			d.Authenticate = auth.Authenticate
		}
		if _, err := d.DialWithConn(ctx, conn, "tcp", cm.targetAddr); err != nil {
			conn.Close()
			return nil, err
		}
	case cm.targetScheme == "http":
		pconn.isProxy = true
		if pa := cm.proxyAuth(); pa != "" {
			pconn.mutateHeaderFunc = func(h Header) {
				h.Set("Proxy-Authorization", pa)
			}
		}
	case cm.targetScheme == "https":
		conn := pconn.conn
		var hdr Header
		if t.GetProxyConnectHeader != nil {
			var err error
			hdr, err = t.GetProxyConnectHeader(ctx, cm.proxyURL, cm.targetAddr)
			if err != nil {
				conn.Close()
				return nil, err
			}
		} else {
			hdr = t.ProxyConnectHeader
		}
		if hdr == nil {
			hdr = make(Header)
		}
		if pa := cm.proxyAuth(); pa != "" {
			hdr = hdr.Clone()
			hdr.Set("Proxy-Authorization", pa)
		}
		connectReq := &Request{
			Method: "CONNECT",
			URL:    &url.URL{Opaque: cm.targetAddr},
			Host:   cm.targetAddr,
			Header: hdr,
		}

		// 如果没有完成频道（没有截止日期或取消
		// 可能来自调用者），至少在此处设置一些（长）
		// 超时。这将确保在TCP连接后，如果连接停止回复
		// 并泄漏一个goroutine。
		// 我们不会永远阻止
		connectCtx := ctx
		if ctx.Done() == nil {
			newCtx, cancel := context.WithTimeout(ctx, 1*time.Minute)
			defer cancel()
			connectCtx = newCtx
		}

		didReadResponse := make(chan struct{}) // 连接写入+读取完成或失败后关闭
		var (
			resp *Response
			err  error // 写入或读取错误
		)
		// 写入连接请求并读取响应。
		go func() {
			defer close(didReadResponse)
			err = connectReq.Write(conn)
			if err != nil {
				return
			}
			// 可以在这里使用和丢弃缓冲读取器，因为
			// TLS服务器在与用户通话之前不会说话。
			br := bufio.NewReader(conn)
			resp, err = ReadResponse(br, connectReq)
		}()
		select {
		case <-connectCtx.Done():
			conn.Close()
			<-didReadResponse
			return nil, connectCtx.Err()
		case <-didReadResponse:
			// resp or err现在设置
		}
		if err != nil {
			conn.Close()
			return nil, err
		}
		if resp.StatusCode != 200 {
			_, text, ok := strings.Cut(resp.Status, " ")
			conn.Close()
			if !ok {
				return nil, errors.New("unknown status code")
			}
			return nil, errors.New(text)
		}
	}

	if cm.proxyURL != nil && cm.targetScheme == "https" {
		if err := pconn.addTLS(ctx, cm.tlsHost(), trace); err != nil {
			return nil, err
		}
	}

	if s := pconn.tlsState; s != nil && s.NegotiatedProtocolIsMutual && s.NegotiatedProtocol != "" {
		if next, ok := t.TLSNextProto[s.NegotiatedProtocol]; ok {
			alt := next(cm.targetAddr, pconn.conn.(*tls.Conn))
			if e, ok := alt.(erringRoundTripper); ok {
				// pconn。conn已于下一步关闭（http2configureTransports.upgradeFn）。
				return nil, e.RoundTripErr()
			}
			return &persistConn{t: t, cacheKey: pconn.cacheKey, alt: alt}, nil
		}
	}

	pconn.br = bufio.NewReaderSize(pconn, t.readBufferSize())
	pconn.bw = bufio.NewWriterSize(persistConnWriter{pconn}, t.writeBufferSize())

	go pconn.readLoop()
	go pconn.writeLoop()
	return pconn, nil
}

// 作者是io。pc.bw写给的作者。
// 它累加写入底层conn的字节数，
// 因此重试逻辑可以确定是否有任何字节通过
// 线路。
// 这正好是一个指针字段宽，因此它可以进入接口
// 而无需分配。
type persistConnWriter struct {
	pc *persistConn
}

func (w persistConnWriter) Write(p []byte) (n int, err error) {
	n, err = w.pc.conn.Write(p)
	w.pc.nwrite += int64(n)
	return
}

// ReadFrom将persistConnWriter的底层Conn暴露给io。复制，如果
// 控制器执行io。ReaderFrom，它可以利用
// 等优化功能，例如sendfile。
func (w persistConnWriter) ReadFrom(r io.Reader) (n int64, err error) {
	n, err = io.Copy(w.pc.conn, r)
	w.pc.nwrite += n
	return
}

var _ io.ReaderFrom = (*persistConnWriter)(nil)

// connectMethod是映射键（以字符串形式），用于在后续HTTP请求中保持持久的
// TCP连接处于活动状态。
// 
// 连接方法可以是以下类型：
// 
// connectMethod。键（）。String（）Description 
// /-------------------------------------------------
// /| http | foo。com http直接到服务器，没有代理
// /|https | foo。com https直接连接到服务器，无代理
// | https，h1 | foo。com https直接连接到服务器，无HTTP/2，无代理
// http:
// http:
// socks5:
// socks5:
// https:
// https:
// 
type connectMethod struct {
	_            incomparable
	proxyURL     *url.URL // nil表示没有代理，否则完整代理URL 
	targetScheme string   // “http”或“https”
	// 如果proxyURL指定了http或https代理，并且targetScheme是http（非https），
	// 则连接方法密钥中不包括targetAddress，因为套接字
	// 可以被用于不同的targetAddress值。
	targetAddr string
	onlyH1     bool // 是否禁用HTTP/2并强制HTTP/1 
}

func (cm *connectMethod) key() connectMethodKey {
	proxyStr := ""
	targetAddr := cm.targetAddr
	if cm.proxyURL != nil {
		proxyStr = cm.proxyURL.String()
		if (cm.proxyURL.Scheme == "http" || cm.proxyURL.Scheme == "https") && cm.targetScheme == "http" {
			targetAddr = ""
		}
	}
	return connectMethodKey{
		proxy:  proxyStr,
		scheme: cm.targetScheme,
		addr:   targetAddr,
		onlyH1: cm.onlyH1,
	}
}

// scheme返回第一跳方案：HTTP、https或socks5 
func (cm *connectMethod) scheme() string {
	if cm.proxyURL != nil {
		return cm.proxyURL.Scheme
	}
	return cm.targetScheme
}

// addr返回我们需要TCP连接的第一跳“主机：端口”。
func (cm *connectMethod) addr() string {
	if cm.proxyURL != nil {
		return canonicalAddr(cm.proxyURL)
	}
	return cm.targetAddr
}

// tlsHost返回主机名，以与对等方的
// TLS证书匹配。
func (cm *connectMethod) tlsHost() string {
	h := cm.targetAddr
	if hasPort(h) {
		h = h[:strings.LastIndex(h, ":")]
	}
	return h
}

// connectMethodKey是connectMethod的映射键版本，带有一个
// 字符串化的代理URL（或空字符串），而不是指向
// URL的指针。
type connectMethodKey struct {
	proxy, scheme, addr string
	onlyH1              bool
}

func (k connectMethodKey) String() string {
	// 仅用于测试。
	var h1 string
	if k.onlyH1 {
		h1 = ",h1"
	}
	return fmt.Sprintf("%s|%s%s|%s", k.proxy, k.scheme, h1, k.addr)
}

// persistConn包装一个连接，通常是一个持久连接
// （但也可以用于非保持活动的请求）
type persistConn struct {
	// alt可选地指定TLS NextProto往返器。
	// 这用于今天的HTTP/2和以后的协议。
	// 如果不是nil，则其余字段未使用。
	alt RoundTripper

	t         *Transport
	cacheKey  connectMethodKey
	conn      net.Conn
	tlsState  *tls.ConnectionState
	br        *bufio.Reader       // 从康涅狄格州
	bw        *bufio.Writer       // 到康涅狄格州
	nwrite    int64               // 写入的字节
	reqch     chan requestAndChan // 通过往返写入；由readLoop 
	writech   chan writeRequest   // /读取，由往返编写；WriteLop 
	closech   chan struct{}       // 康涅狄格州关闭时关闭
	isProxy   bool
	sawEOF    bool  // 我们是否见过康涅狄格州的EOF；归readLoop 
	readLimit int64 // 允许读取的字节；由readLoop拥有
	// writeErrCh将请求写入错误（通常为零）
	// 从writeLoop goroutine传递到readLoop，后者将
	// 传递给res.Body reader，后者随后使用它来决定
	// 是否可以重用连接。第7569期。
	writeErrCh chan error

	writeLoopDone chan struct{} // 写入循环结束时关闭

	// 两者都由传输保护。idleMu:
	idleAt    time.Time   // 最后一次变为空闲
	idleTimer *time.Timer // 保持AfterFunc关闭

	mu                   sync.Mutex // 保护以下字段
	numExpectedResponses int
	closed               error // 关闭连接时设置非零
	canceledErr          error // 取消连接时设置非零
	broken               bool  // 此连接发生错误；标记为已损坏，因此不能重复使用。
	reused               bool  // conn是否有成功的请求/响应并且正在被重用。
	// mutateHeaderFunc是一个可选函数，用于在写入每个出站请求之前修改额外的
	// 头。（
	// 原往返请求未修改）
	mutateHeaderFunc func(Header)
}

func (pc *persistConn) maxHeaderResponseSize() int64 {
	if v := pc.t.MaxResponseHeaderBytes; v != 0 {
		return v
	}
	return 10 << 20 // 保守违约；与http2 
}

func (pc *persistConn) Read(p []byte) (n int, err error) {
	if pc.readLimit <= 0 {
		return 0, fmt.Errorf("read limit of %d bytes exhausted", pc.maxHeaderResponseSize())
	}
	if int64(len(p)) > pc.readLimit {
		p = p[:pc.readLimit]
	}
	n, err = pc.conn.Read(p)
	if err == io.EOF {
		pc.sawEOF = true
	}
	pc.readLimit -= int64(n)
	return
}

// 相同，isbreak报告此连接是否处于已知的断开状态。
func (pc *persistConn) isBroken() bool {
	pc.mu.Lock()
	b := pc.closed != nil
	pc.mu.Unlock()
	return b
}

// 如果由于
// CancelRequest或由于上下文取消而关闭连接，Canceld返回非零。
func (pc *persistConn) canceled() error {
	pc.mu.Lock()
	defer pc.mu.Unlock()
	return pc.canceledErr
}

// isReused报告以前是否使用过此连接。
func (pc *persistConn) isReused() bool {
	pc.mu.Lock()
	r := pc.reused
	pc.mu.Unlock()
	return r
}

func (pc *persistConn) gotIdleConnTrace(idleAt time.Time) (t httptrace.GotConnInfo) {
	pc.mu.Lock()
	defer pc.mu.Unlock()
	t.Reused = pc.reused
	t.Conn = pc.conn
	t.WasIdle = true
	if !idleAt.IsZero() {
		t.IdleTime = time.Since(idleAt)
	}
	return
}

func (pc *persistConn) cancelRequest(err error) {
	pc.mu.Lock()
	defer pc.mu.Unlock()
	pc.canceledErr = err
	pc.closeLocked(errRequestCanceled)
}

// closeConnIfStillIdle在连接仍然处于空闲状态时关闭连接。
// 这就是所谓的persistConn的idleTimer，在其
// 自己的goroutine中运行。
func (pc *persistConn) closeConnIfStillIdle() {
	t := pc.t
	t.idleMu.Lock()
	defer t.idleMu.Unlock()
	if _, ok := t.idleLRU.m[pc]; !ok {
		// 不空闲。
		return
	}
	t.removeIdleConnLocked(pc)
	pc.close(errIdleConnTimeout)
}

// mapRoundTripError为
// persistConn返回适当的错误值。往返。
// 
// 提供的错误是（*persistConn）出现的第一个错误。往返
// 碰巧从select语句中收到。
// 
// 起始字节写入值应为往返
func (pc *persistConn) mapRoundTripError(req *transportRequest, startBytesWritten int64, err error) error {
	if err == nil {
		return nil
	}

	// 等待WriteLop goroutine终止以避免数据
	// 在失败时对请求进行变异的呼叫者进行竞争。
	// 
	// 当在pc中重新扫描时。往返，因此rc。ch收到一个非零错误的响应ADERROR 
	// 这意味着persistConn要么关闭
	// 要么关闭。因此，等待pc.WriteLopDone是安全的，因为所有呼叫者
	// 关闭closech，从而确保WriteLop返回。
	<-pc.writeLoopDone

	// 如果请求被取消，这比网络
	// 故障要好，故障可能是由于断开
	// 连接造成的。
	if cerr := pc.canceled(); cerr != nil {
		return cerr
	}

	// 查看是否显式设置了错误。
	req.mu.Lock()
	reqErr := req.err
	req.mu.Unlock()
	if reqErr != nil {
		return reqErr
	}

	if err == errServerClosedIdle {
		// 不要修饰
		return err
	}

	if _, ok := err.(transportReadFromServerError); ok {
		if pc.nwrite == startBytesWritten {
			return nothingWrittenError{err}
		}
		// 不要修饰
		return err
	}
	if pc.isBroken() {
		if pc.nwrite == startBytesWritten {
			return nothingWrittenError{err}
		}
		return fmt.Errorf("net/http: HTTP/1.x transport connection broken: %v", err)
	}
	return err
}

// errCallerRownConn是一个内部哨兵错误，当我们将
// 从一个可写响应中移出时使用。把尸体交给打电话的人。我们用它来阻止
// 关闭网络。现在属于来电者的Conn。
var errCallerOwnsConn = errors.New("read loop ending; caller owns writable underlying conn")

func (pc *persistConn) readLoop() {
	closeErr := errReadLoopExiting // 默认值，如果未更改为低于
	defer func() {
		pc.close(closeErr)
		pc.t.removeIdleConn(pc)
	}()

	tryPutIdleConn := func(trace *httptrace.ClientTrace) bool {
		if err := pc.t.tryPutIdleConn(pc); err != nil {
			closeErr = err
			if trace != nil && trace.PutIdleConn != nil && err != errKeepAlivesDisabled {
				trace.PutIdleConn(err)
			}
			return false
		}
		if trace != nil && trace.PutIdleConn != nil {
			trace.PutIdleConn(nil)
		}
		return true
	}

	// eofc用于阻止调用方goroutines从响应中读取。Body 
	// 在EOF上，直到此goroutines（可能）将连接
	// 添加回空闲池。
	eofc := make(chan struct{})
	defer close(eofc) // 在循环开始之前，解除对错误的读卡器的阻止

	// 读取此文件一次。（为了避免测试中的竞争）
	testHookMu.Lock()
	testHookReadLoopBeforeNextRead := testHookReadLoopBeforeNextRead
	testHookMu.Unlock()

	alive := true
	for alive {
		pc.readLimit = pc.maxHeaderResponseSize()
		_, err := pc.br.Peek(1)

		pc.mu.Lock()
		if pc.numExpectedResponses == 0 {
			pc.readLoopPeekFailLocked(err)
			pc.mu.Unlock()
			return
		}
		pc.mu.Unlock()

		rc := <-pc.reqch
		trace := httptrace.ContextClientTrace(rc.req.Context())

		var resp *Response
		if err == nil {
			resp, err = pc.readResponse(rc, trace)
		} else {
			err = transportReadFromServerError{err}
			closeErr = err
		}

		if err != nil {
			if pc.readLimit <= 0 {
				err = fmt.Errorf("net/http: server response headers exceeded %d bytes; aborted", pc.maxHeaderResponseSize())
			}

			select {
			case rc.ch <- responseAndError{err: err}:
			case <-rc.callerGone:
				return
			}
			return
		}
		pc.readLimit = maxInt64 // 实际上，响应机构没有限制

		pc.mu.Lock()
		pc.numExpectedResponses--
		pc.mu.Unlock()

		bodyWritable := resp.bodyIsWritable()
		hasBody := rc.req.Method != "HEAD" && resp.ContentLength != 0

		if resp.Close || rc.req.Close || resp.StatusCode <= 199 || bodyWritable {
			// 如果任何一方请求关闭
			// 或者我们得到意外的信息（1xx）响应，请不要继续关注错误。
			// 上面已经处理了状态码100。
			alive = false
		}

		if !hasBody || bodyWritable {
			replaced := pc.t.replaceReqCanceler(rc.cancelKey, nil)

			// 在我们发送响应之前，将空闲连接放回池中
			// 因此，如果他们快速处理并发出另一个请求，他们将
			// 获得相同的连接。但我们使用无缓冲通道'rc'
			// 来保证持久连接。往返已退出其精选
			// 可能正在等待此persistConn关闭。
			alive = alive &&
				!pc.sawEOF &&
				pc.wroteRequest() &&
				replaced && tryPutIdleConn(trace)

			if bodyWritable {
				closeErr = errCallerOwnsConn
			}

			select {
			case rc.ch <- responseAndError{res: resp}:
			case <-rc.callerGone:
				return
			}

			// 现在他们已经从无缓冲频道读取了，他们安全地退出了同样等待这个goroutine死亡的选择，因此，如果需要，我们现在可以退出（如果alive为false）
			testHookReadLoopBeforeNextRead()
			continue
		}

		waitForBodyRead := make(chan bool, 2)
		body := &bodyEOFSignal{
			body: resp.Body,
			earlyCloseFn: func() error {
				waitForBodyRead <- false
				<-eofc // 将通过函数末尾的延迟调用关闭
				return nil

			},
			fn: func(err error) error {
				isEOF := err == io.EOF
				waitForBodyRead <- isEOF
				if isEOF {
					<-eofc // 请参阅eofc声明上方的注释
				} else if err != nil {
					if cerr := pc.canceled(); cerr != nil {
						return cerr
					}
				}
				return err
			},
		}

		resp.Body = body
		if rc.addedGzip && ascii.EqualFold(resp.Header.Get("Content-Encoding"), "gzip") {
			resp.Body = &gzipReader{body: body}
			resp.Header.Del("Content-Encoding")
			resp.Header.Del("Content-Length")
			resp.ContentLength = -1
			resp.Uncompressed = true
		}

		select {
		case rc.ch <- responseAndError{res: resp}:
		case <-rc.callerGone:
			return
		}

		// 然后循环到此函数顶部并查看
		// bufio。Reader，等待调用程序goroutine完成
		// 读取响应正文。（或用于取消或死亡）
		select {
		case bodyEOF := <-waitForBodyRead:
			replaced := pc.t.replaceReqCanceler(rc.cancelKey, nil) // 在pc可能返回空闲池之前
			alive = alive &&
				bodyEOF &&
				!pc.sawEOF &&
				pc.wroteRequest() &&
				replaced && tryPutIdleConn(trace)
			if bodyEOF {
				eofc <- struct{}{}
			}
		case <-rc.req.Cancel:
			alive = false
			pc.t.CancelRequest(rc.req)
		case <-rc.req.Context().Done():
			alive = false
			pc.t.cancelRequest(rc.cancelKey, rc.req.Context().Err())
		case <-pc.closech:
			alive = false
		}

		testHookReadLoopBeforeNextRead()
	}
}

func (pc *persistConn) readLoopPeekFailLocked(peekErr error) {
	if pc.closed != nil {
		return
	}
	if n := pc.br.Buffered(); n > 0 {
		buf, _ := pc.br.Peek(n)
		if is408Message(buf) {
			pc.closeLocked(errServerClosedIdle)
			return
		} else {
			log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v", buf, peekErr)
		}
	}
	if peekErr == io.EOF {
		// 常见情况。
		pc.closeLocked(errServerClosedIdle)
	} else {
		pc.closeLocked(fmt.Errorf("readLoopPeekFailLocked: %v", peekErr))
	}
}

// is408Message报告buf是否具有
// HTTP 408请求超时响应的前缀。
// 见戈朗。org/issue/32310。
func is408Message(buf []byte) bool {
	if len(buf) < len("HTTP/1.x 408") {
		return false
	}
	if string(buf[:7]) != "HTTP/1." {
		return false
	}
	return string(buf[8:12]) == " 408"
}

// readResponse从服务器读取一个HTTP响应（如果是“Expect:
// 100 continue”，则读取两个）。它返回最后一个非100。
// 跟踪是可选的。
func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTrace) (resp *Response, err error) {
	if trace != nil && trace.GotFirstResponseByte != nil {
		if peek, err := pc.br.Peek(1); err == nil && len(peek) == 1 {
			trace.GotFirstResponseByte()
		}
	}
	num1xx := 0               // 接收到的信息性1x头数
	const max1xxResponses = 5 // 任意绑定信息性响应数

	continueCh := rc.continueCh
	for {
		resp, err = ReadResponse(pc.br, rc.req)
		if err != nil {
			return
		}
		resCode := resp.StatusCode
		if continueCh != nil {
			if resCode == 100 {
				if trace != nil && trace.Got100Continue != nil {
					trace.Got100Continue()
				}
				continueCh <- struct{}{}
				continueCh = nil
			} else if resCode >= 200 {
				close(continueCh)
				continueCh = nil
			}
		}
		is1xx := 100 <= resCode && resCode <= 199
		// 将101视为终端状态，请参阅问题26161 
		is1xxNonTerminal := is1xx && resCode != StatusSwitchingProtocols
		if is1xxNonTerminal {
			num1xx++
			if num1xx > max1xxResponses {
				return nil, errors.New("net/http: too many 1xx informational responses")
			}
			pc.readLimit = pc.maxHeaderResponseSize() // 重置限制
			if trace != nil && trace.Got1xxResponse != nil {
				if err := trace.Got1xxResponse(resCode, textproto.MIMEHeader(resp.Header)); err != nil {
					return nil, err
				}
			}
			continue
		}
		break
	}
	if resp.isProtocolSwitch() {
		resp.Body = newReadWriteCloserBody(pc.br, pc.conn)
	}

	resp.TLS = pc.tlsState
	return
}

// waitForContinue返回阻止函数，直到
// 任何响应、超时或连接关闭。在其中任何一个之后，
// 该函数返回一个bool，指示是否应该发送正文。
func (pc *persistConn) waitForContinue(continueCh <-chan struct{}) func() bool {
	if continueCh == nil {
		return nil
	}
	return func() bool {
		timer := time.NewTimer(pc.t.ExpectContinueTimeout)
		defer timer.Stop()

		select {
		case _, ok := <-continueCh:
			return ok
		case <-timer.C:
			return true
		case <-pc.closech:
			return false
		}
	}
}

func newReadWriteCloserBody(br *bufio.Reader, rwc io.ReadWriteCloser) io.ReadWriteCloser {
	body := &readWriteCloserBody{ReadWriteCloser: rwc}
	if br.Buffered() != 0 {
		body.br = br
	}
	return body
}

// readWriteCloserBody是响应。当我们想要
// 通过基础
// 连接（TCP，除非使用自定义拨号程序）为用户提供对正文的写访问权限时使用的正文类型。这就是
// 响应的具体类型。101开关
// 协议响应上的主体，用于WebSocket、h2c等。
type readWriteCloserBody struct {
	_  incomparable
	br *bufio.Reader // 直到使用空
	io.ReadWriteCloser
}

func (b *readWriteCloserBody) Read(p []byte) (n int, err error) {
	if b.br != nil {
		if n := b.br.Buffered(); len(p) > n {
			p = p[:n]
		}
		n, err = b.br.Read(p)
		if b.br.Buffered() == 0 {
			b.br = nil
		}
		return n, err
	}
	return b.ReadWriteCloser.Read(p)
}

// nothingWrittenError包装写入错误，最终写入零字节。
type nothingWrittenError struct {
	error
}

func (pc *persistConn) writeLoop() {
	defer close(pc.writeLoopDone)
	for {
		select {
		case wr := <-pc.writech:
			startBytesWritten := pc.nwrite
			err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra, pc.waitForContinue(wr.continueCh))
			if bre, ok := err.(requestBodyReadError); ok {
				err = bre.error
				// 读取用户的
				// 请求时出错。身体是重中之重。
				// 在下面的
				// 频道发送或调用
				// pc.close（）之前，请将其设置在此处，这会中断
				// 连接并导致其他
				// 错误。
				wr.req.setError(err)
			}
			if err == nil {
				err = pc.bw.Flush()
			}
			if err != nil {
				if pc.nwrite == startBytesWritten {
					err = nothingWrittenError{err}
				}
			}
			pc.writeErrCh <- err // 给body reader，它可能会循环我们
			wr.ch <- err         // 给往返函数
			if err != nil {
				pc.close(err)
				return
			}
		case <-pc.closech:
			return
		}
	}
}

// MaxWriteWaitBeforeConreuse是传输往返的时间长度
// 将等待查看请求的正文。从服务器获得
// 响应后写入结果。请参阅（*persistConn）中的注释。谢谢你的请求。
const maxWriteWaitBeforeConnReuse = 50 * time.Millisecond

// wroteRequest是在回收连接之前检查上一次写入
// （来自上面的WriteLop）是否发生并成功。
func (pc *persistConn) wroteRequest() bool {
	select {
	case err := <-pc.writeErrCh:
		// 常见情况：写操作发生在响应之前，因此
		// 避免创建计时器。
		return err == nil
	default:
		// 罕见的情况：请求是用上面的writeLoop写的，但在发送到pc.writeErrCh之前，读者阅读了它
		// 全部，处理了它，并打电话给我们。在这种情况下，给
		// write goroutine一点时间来完成发送。
		// 
		// 不太罕见的案例：我们也看到了
		// 第7569期的合法案例，作者仍在写作（或暂停），
		// 但服务器已经回复。在这种情况下，我们不想等待太久，我们想返回false，这样这个连接就不会被重复使用。
		t := time.NewTimer(maxWriteWaitBeforeConnReuse)
		defer t.Stop()
		select {
		case err := <-pc.writeErrCh:
			return err == nil
		case <-t.C:
			return false
		}
	}
}

// responseAndError是从HTTP/1服务器读取的goroutine如何与执行往返的goroutine通信。
type responseAndError struct {
	_   incomparable
	res *Response // 否则使用此响应（参见res方法）
	err error
}

type requestAndChan struct {
	_         incomparable
	req       *Request
	cancelKey cancelKey
	ch        chan responseAndError // 无缓冲；always send in select on callerGone 

	// 传输（与用户客户端代码相反）
	// 是否添加了接受编码gzip头。如果传输
	// 设置它，只有这样我们才能透明地解码gzip。
	addedGzip bool

	// Expect的可选阻塞chan:100 continue（用于发送）。
	// 如果请求有一个“Expect:100 continue”头，并且
	// 服务器响应100 continue，则readLoop通过此chan向writeLoop发送一个值
	// 。
	continueCh chan<- struct{}

	callerGone <-chan struct{} // 当往返调用方返回
}

// 时关闭。调用方的goroutine向
// writeLoop的goroutine发送写入请求，而读取循环
// 同时等待写入响应和服务器的
// 回复。
type writeRequest struct {
	req *transportRequest
	ch  chan<- error

	// Expect的可选阻塞通道：100 continue（用于接收）。
	// 如果不是零，WriteLop将阻止发送请求正文，直到
	// 它收到来自此chan的消息。
	continueCh <-chan struct{}
}

type httpError struct {
	err     string
	timeout bool
}

func (e *httpError) Error() string   { return e.err }
func (e *httpError) Timeout() bool   { return e.timeout }
func (e *httpError) Temporary() bool { return true }

var errTimeout error = &httpError{err: "net/http: timeout awaiting response headers", timeout: true}

// 错误请求取消设置为与h2中的相同，以便于
// 测试。
var errRequestCanceled = http2errRequestCanceled
var errRequestCanceledConn = errors.New("net/http: request canceled while waiting for connection") // 待办事项：统一？

func nop() {}

// 测试挂钩。总是非零。
var (
	testHookEnterRoundTrip   = nop
	testHookWaitResLoop      = nop
	testHookRoundTripRetried = nop
	testHookPrePendingDial   = nop
	testHookPostPendingDial  = nop

	testHookMu                     sync.Locker = fakeLocker{} // 如果调用者没有设置自己的
	testHookReadLoopBeforeNextRead             = nop
)

func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
	testHookEnterRoundTrip()
	if !pc.t.replaceReqCanceler(req.cancelKey, pc.cancelRequest) {
		pc.t.putOrCloseIdleConn(pc)
		return nil, errRequestCanceled
	}
	pc.mu.Lock()
	pc.numExpectedResponses++
	headerFn := pc.mutateHeaderFunc
	pc.mu.Unlock()

	if headerFn != nil {
		headerFn(req.extraHeaders())
	}

	// 的警卫请求压缩版本。如果我们是
	// 接受编码的值，则跟随
	// 请求的层，我们只会尝试解压gzip流。
	requestedGzip := false
	if !pc.t.DisableCompression &&
		req.Header.Get("Accept-Encoding") == "" &&
		req.Header.Get("Range") == "" &&
		req.Method != "HEAD" {
		// 仅请求gzip，不请求泄气。Deflate模棱两可，
		// 无论如何都没有得到普遍支持。
		// 请参见：https:
		// 
		// 注意，我们不请求HEAD请求，
		// 由于nginx中的一个错误：
		// https:
		// https:
		// 
		// 如果请求的范围很大，我们不请求gzip，因为自动解码Gzip文档的一部分会失败。请参阅https:
		requestedGzip = true
		req.extraHeaders().Set("Accept-Encoding", "gzip")
	}

	var continueCh chan struct{}
	if req.ProtoAtLeast(1, 1) && req.Body != nil && req.expectsContinue() {
		continueCh = make(chan struct{}, 1)
	}

	if pc.t.DisableKeepAlives &&
		!req.wantsClose() &&
		!isProtocolSwitchHeader(req.Header) {
		req.extraHeaders().Set("Connection", "close")
	}

	gone := make(chan struct{})
	defer close(gone)

	defer func() {
		if err != nil {
			pc.t.setReqCanceler(req.cancelKey, nil)
		}
	}()

	const debugRoundTrip = false

	// 在等待响应的同时编写请求，
	// 以防服务器决定在阅读完整的
	// 请求正文之前进行回复。
	startBytesWritten := pc.nwrite
	writeErrCh := make(chan error, 1)
	pc.writech <- writeRequest{req, writeErrCh, continueCh}

	resc := make(chan responseAndError)
	pc.reqch <- requestAndChan{
		req:        req.Request,
		cancelKey:  req.cancelKey,
		ch:         resc,
		addedGzip:  requestedGzip,
		continueCh: continueCh,
		callerGone: gone,
	}

	var respHeaderTimer <-chan time.Time
	cancelChan := req.Request.Cancel
	ctxDoneChan := req.Context().Done()
	pcClosed := pc.closech
	canceled := false
	for {
		testHookWaitResLoop()
		select {
		case err := <-writeErrCh:
			if debugRoundTrip {
				req.logf("writeErrCh resv: %T/%#v", err, err)
			}
			if err != nil {
				pc.close(fmt.Errorf("write error: %v", err))
				return nil, pc.mapRoundTripError(req, startBytesWritten, err)
			}
			if d := pc.t.ResponseHeaderTimeout; d > 0 {
				if debugRoundTrip {
					req.logf("starting timer for %v", d)
				}
				timer := time.NewTimer(d)
				defer timer.Stop() // 防止泄漏
				respHeaderTimer = timer.C
			}
		case <-pcClosed:
			pcClosed = nil
			if canceled || pc.t.replaceReqCanceler(req.cancelKey, nil) {
				if debugRoundTrip {
					req.logf("closech recv: %T %#v", pc.closed, pc.closed)
				}
				return nil, pc.mapRoundTripError(req, startBytesWritten, pc.closed)
			}
		case <-respHeaderTimer:
			if debugRoundTrip {
				req.logf("timeout waiting for response headers.")
			}
			pc.close(errTimeout)
			return nil, errTimeout
		case re := <-resc:
			if (re.res == nil) == (re.err == nil) {
				panic(fmt.Sprintf("internal error: exactly one of res or err should be set; nil=%v", re.res == nil))
			}
			if debugRoundTrip {
				req.logf("resc recv: %p, %T/%#v", re.res, re.err, re.err)
			}
			if re.err != nil {
				return nil, pc.mapRoundTripError(req, startBytesWritten, re.err)
			}
			return re.res, nil
		case <-cancelChan:
			canceled = pc.t.cancelRequest(req.cancelKey, errRequestCanceled)
			cancelChan = nil
		case <-ctxDoneChan:
			canceled = pc.t.cancelRequest(req.cancelKey, req.Context().Err())
			cancelChan = nil
			ctxDoneChan = nil
		}
	}
}

// tLogKey是一个带有value key的上下文，用于包含
// a t.Logf func的测试调试上下文。参见导出测试。go的要求。Witt方法。
type tLogKey struct{}

func (tr *transportRequest) logf(format string, args ...any) {
	if logf, ok := tr.Request.Context().Value(tLogKey{}).(func(string, ...any)); ok {
		logf(time.Now().Format(time.RFC3339Nano)+": "+format, args...)
	}
}

// 标记此连接已成功用于
// 请求和响应。
func (pc *persistConn) markReused() {
	pc.mu.Lock()
	pc.reused = true
	pc.mu.Unlock()
}

// 关闭底层TCP连接并关闭
// pc.closech通道。
// 
// 提供的错误仅用于测试和调试；在正常的
// 情况下，用户永远不会看到它。
func (pc *persistConn) close(err error) {
	pc.mu.Lock()
	defer pc.mu.Unlock()
	pc.closeLocked(err)
}

func (pc *persistConn) closeLocked(err error) {
	if err == nil {
		panic("nil error")
	}
	pc.broken = true
	if pc.closed == nil {
		pc.closed = err
		pc.t.decConnsPerHost(pc.cacheKey)
		// 关闭HTTP/1（pc.alt==nil）连接。
		// HTTP/2会自行关闭连接。
		if pc.alt == nil {
			if err != errCallerOwnsConn {
				pc.conn.Close()
			}
			close(pc.closech)
		}
	}
	pc.mutateHeaderFunc = nil
}

var portMap = map[string]string{
	"http":   "80",
	"https":  "443",
	"socks5": "1080",
}

// CanonicalAddress返回url。主机，但始终带有“：port”后缀
func canonicalAddr(url *url.URL) string {
	addr := url.Hostname()
	if v, err := idnaASCII(addr); err == nil {
		addr = v
	}
	port := url.Port()
	if port == "" {
		port = portMap[url.Scheme]
	}
	return net.JoinHostPort(addr, port)
}

// bodyEOFSignal是HTTP/1传输在读取响应
// body时使用的，以确保我们在
// 继续并再次读取连接之前看到响应正文的结尾。
// 
// 它包装了一个ReadCloser，但最多运行fn（如果非零）一次
// 就在其最终（产生错误的）读取或关闭调用
// 返回之前。fn应返回新错误，以从读取或关闭返回。
// 
// 如果earlyCloseFn为非零且在io之前调用Close。EOF是
// 可见，调用earlyCloseFn而不是fn，其返回值是
// 来自Close的返回值。
type bodyEOFSignal struct {
	body         io.ReadCloser
	mu           sync.Mutex        // 保护以下4个字段
	closed       bool              // 是否已调用Close 
	rerr         error             // 粘性读取错误
	fn           func(error) error // 读取io时错误为零。EOF 
	earlyCloseFn func() error      // 可选的alt Close func用于io。EOF不可见
}

var errReadOnClosedResBody = errors.New("http: read on closed response body")

func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
	es.mu.Lock()
	closed, rerr := es.closed, es.rerr
	es.mu.Unlock()
	if closed {
		return 0, errReadOnClosedResBody
	}
	if rerr != nil {
		return 0, rerr
	}

	n, err = es.body.Read(p)
	if err != nil {
		es.mu.Lock()
		defer es.mu.Unlock()
		if es.rerr == nil {
			es.rerr = err
		}
		err = es.condfn(err)
	}
	return
}

func (es *bodyEOFSignal) Close() error {
	es.mu.Lock()
	defer es.mu.Unlock()
	if es.closed {
		return nil
	}
	es.closed = true
	if es.earlyCloseFn != nil && es.rerr != io.EOF {
		return es.earlyCloseFn()
	}
	err := es.body.Close()
	return es.condfn(err)
}

// 呼叫方必须持有es。穆。
func (es *bodyEOFSignal) condfn(err error) error {
	if es.fn == nil {
		return err
	}
	err = es.fn(err)
	es.fn = nil
	return err
}

// gzip阅读器包装了一个响应体，这样它就可以懒洋洋地调用gzip。第一次调用NewReader读取
type gzipReader struct {
	_    incomparable
	body *bodyEOFSignal // 底层HTTP/1响应体框架
	zr   *gzip.Reader   // 延迟初始化gzip读取器
	zerr error          // 来自gzip的任何错误。新读者；粘性
}

func (gz *gzipReader) Read(p []byte) (n int, err error) {
	if gz.zr == nil {
		if gz.zerr == nil {
			gz.zr, gz.zerr = gzip.NewReader(gz.body)
		}
		if gz.zerr != nil {
			return 0, gz.zerr
		}
	}

	gz.body.mu.Lock()
	if gz.body.closed {
		err = errReadOnClosedResBody
	}
	gz.body.mu.Unlock()

	if err != nil {
		return 0, err
	}
	return gz.zr.Read(p)
}

func (gz *gzipReader) Close() error {
	return gz.body.Close()
}

type tlsHandshakeTimeoutError struct{}

func (tlsHandshakeTimeoutError) Timeout() bool   { return true }
func (tlsHandshakeTimeoutError) Temporary() bool { return true }
func (tlsHandshakeTimeoutError) Error() string   { return "net/http: TLS handshake timeout" }

// fakeLocker是一个同步器。什么都不做的储物柜。它用于在未被测试时保护
// 仅测试字段，以避免运行时原子
// 开销。
type fakeLocker struct{}

func (fakeLocker) Lock()   {}
func (fakeLocker) Unlock() {}

// cloneTLSConfig返回cfg的浅层克隆或新的零tls。如果
// cfg为零，则进行配置。即使TLS 
// 客户端或服务器正在使用cfg，也可以安全地调用此命令。
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
	if cfg == nil {
		return &tls.Config{}
	}
	return cfg.Clone()
}

type connLRU struct {
	ll *list.List // 列表。要素值类型为*persisconn 
	m  map[*persistConn]*list.Element
}

// add将pc添加到链接列表的头部。
func (cl *connLRU) add(pc *persistConn) {
	if cl.ll == nil {
		cl.ll = list.New()
		cl.m = make(map[*persistConn]*list.Element)
	}
	ele := cl.ll.PushFront(pc)
	if _, ok := cl.m[pc]; ok {
		panic("persistConn was already in LRU")
	}
	cl.m[pc] = ele
}

func (cl *connLRU) removeOldest() *persistConn {
	ele := cl.ll.Back()
	pc := ele.Value.(*persistConn)
	cl.ll.Remove(ele)
	delete(cl.m, pc)
	return pc
}

// remove从cl中删除pc。
func (cl *connLRU) remove(pc *persistConn) {
	if ele, ok := cl.m[pc]; ok {
		cl.ll.Remove(ele)
		delete(cl.m, pc)
	}
}

// len返回缓存中的项目数。
func (cl *connLRU) len() int {
	return len(cl.m)
}
